<?php
declare(strict_types=1);

namespace App\Helper;


/**
 * Class ESearch
 * @package App\Helper
 */
class ESearch
{
    use EsTrait;

    const HOSPITAL_TYPE = '_doc';

    /**
     * 获取别名
     * @return string
     */
    private static function getAlias(): string
    {
        $env = env('APP_ENV', 'local');
        return "test_" . $env;
    }


    /**
     * 医院搜索
     * @param $from
     * @param $limit
     * @param string $cityYYID
     * @param string $provinceYYID
     * @param string $keywords
     * @param array $levels
     * @param array $excludeIds
     * @param array $rangeIds
     * @return array
     */
    public static function searchHospital(
        $from,
        $limit,
        $cityYYID = "",
        $provinceYYID = "",
        $keywords = "",
        $levels = [],
        $excludeIds = [],
        $rangeIds = [],
        $companyYYID = ""
    ): array {
        $log    = Helper::getLogger();
        $alias  = self::getAlias();
        $type   = self::HOSPITAL_TYPE;
        $client = self::getClient();
        if (!self::aliasExists($client, $alias)) {
            $log->error("youyao_hospital_search_error,", ['reason' => 'index_not_exists']);
            return [];
        }

        $start = $from - 1;
        $start < 0 && $start = 0;
        $start = intval($start * $limit);
        if (!empty($excludeIds)) {
            $excludeIds = array_values($excludeIds);
        }
        if (!empty($rangeIds)) {
            $rangeIds = array_values($rangeIds);
        }

        $searchQuery = self::buildSearchQuery(
            $start,
            $limit,
            $cityYYID,
            $provinceYYID,
            $keywords,
            $levels,
            $excludeIds,
            $rangeIds,
            $companyYYID
        );
        $resp        = $client->search([
            'index' => $alias,
            'type'  => $type,
            'body'  => $searchQuery
        ]);
        //这里实际有一个坑， 超出一定数量时会导致
        $total = isset($resp['hits']['total']) ? (int)$resp['hits']['total'] : 0;
        $data  = [];
        if ($total > 0) {
            $data = self::formatStandardHospitalResponseData($resp['hits']['hits']);
        }

        return [$total, $data];
    }

    /**
     * 格式化查询返回到map字段
     * @param array $list
     * @return array
     */
    private static function formatStandardHospitalResponseData(array $list): array
    {
        $data = [];
        foreach ($list as $item) {
            $data[] = [
                'id'                => $item['_source']['id'] ?? 0,
                'yyid'              => $item['_source']['yyid'] ?? '',
                'pro'               => $item['_source']['pro'] ?? '',
                'pro_yyid'          => $item['_source']['pro_yyid'] ?? '',
                'city'              => $item['_source']['city'] ?? '',
                'city_yyid'         => $item['_source']['city_yyid'] ?? '',
                'hospital_name'     => $item['_source']['hospital_name'] ?? '',
                'addr'              => $item['_source']['addr'] ?? '',
                'level'             => $item['_source']['level'] ?? '',
                'old_hospital_name' => $item['_source']['old_hospital_name'] ?? '',
                'old_alias_name'    => $item['_source']['old_alias_name'] ?? '',
                'old_addr'          => $item['_source']['old_addr'] ?? ''
            ];
        };

        return $data;
    }

    /**
     * 生成查询dsl
     * @param $from
     * @param $limit
     * @param string $cityYYID
     * @param string $provinceYYID
     * @param string $keywords
     * @param array $levels
     * @param array $excludeIds
     * @param array $rangeYYIDS
     * @return array
     */
    private static function buildSearchQuery(
        $from,
        $limit,
        $cityYYID = "",
        $provinceYYID = "",
        $keywords = "",
        $levels = [],
        $excludeIds = [],
        $rangeYYIDS = [],
        $companyYYID = ""
    ): array {
        $query = [];
        $must  = [
            [
                "term" => ["status" => ["value" => 1]]
            ]
        ];
        if ($companyYYID) {
            $must[] = [
                [
                    'term' => ['company_yyid' => ['value' => $companyYYID]]
                ]
            ];
        }
        $queryBool = [];

        //城市
        if ($cityYYID) {
            $must[] = [
                "term" => ["city_yyid" => ["value" => $cityYYID]]
            ];
        }
        //省份
        if ($provinceYYID) {
            $must[] = [
                'term' => ['pro_yyid' => ['value' => $provinceYYID]]
            ];
        }
        //搜索
        if ($keywords) {
            $must[] = [
                "match" => ["hospital_name" => $keywords]
            ];
        }
        //等级
        if (!empty($levels)) {
            $should = [];
            foreach ($levels as $level) {
                if (!$level) {
                    $should[] = [
                        'bool' => [
                            'must_not' => [
                                "exists" => ['field' => 'level']
                            ]
                        ]
                    ];
                } else {
                    $should[] = ["match" => ["level" => $level]];
                }
            }
            $must[] = ['bool' => ['should' => $should]];
        }

        //排除id
        if (!empty($rangeYYIDS)) {
            $must[] = [
                'terms' => ['yyid' => $rangeYYIDS]
            ];
        }
        if (!empty($must)) {
            $queryBool['must'] = $must;
        }

        if (!empty($excludeIds)) {
            $queryBool['must_not'] = [
                [
                    "terms" => [
                        "yyid" => $excludeIds
                    ]
                ]
            ];
        }

        if (!empty($queryBool)) {
            $queryBody = ['bool' => $queryBool];
        } else {
            $queryBody = ['match_all' => new \stdClass()];
        }

        return [
            'query' => $queryBody,
            'from'  => $from,
            'size'  => $limit
        ];
    }

    /**
     * 手动map
     * @return array
     */
    public static function getMapping(): array
    {
        return [
            'properties' => [
                'id'                => [
                    'type' => 'integer',
                ],
                'yyid'              => [
                    'type' => 'keyword'
                ],
                # 分析器会将文本分成多个词汇单元，而 ik_smart 分析器则会将文本作为一个完整的词汇单元。

                # ik_max_word 【能拆就拆】会将文本做最细粒度的拆分，比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。

                # ik_smart 【可以不拆的就不拆】会做最粗粒度的拆分，比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
                'pro'               => [
                    'type'            => 'text',
                    "analyzer"        => "ik_max_word",    # 定义了用于在索引过程中对文本进行分析和处理的分析器
                    //                    "search_analyzer" => "ik_smart" # 搜索过程中对查询文本进行分析和处理的分析器
                    "search_analyzer" => "ik_max_word"     # 如果要对这个字段 进行单字搜索 需要用这个
                ],
                'pro_yyid'          => [
                    'type' => 'keyword'
                ],
                'city'              => [
                    'type'            => 'text',
                    "analyzer"        => "ik_max_word",
                    "search_analyzer" => "ik_smart"
                ],
                'city_yyid'         => [
                    'type' => 'keyword'
                ],
                'hospital_name'     => [
                    'type'            => 'text',
                    "analyzer"        => "ik_max_word",
                    "search_analyzer" => "ik_smart"
                ],
                'addr'              => [
                    'type' => 'keyword'
                ],
                'level'             => [
                    'type'            => 'text',
                    "analyzer"        => "ik_max_word",
                    "search_analyzer" => "ik_smart"
                ],
                'old_hospital_name' => [
                    'type'            => 'text',
                    "analyzer"        => "ik_max_word",
                    "search_analyzer" => "ik_smart"
                ],
                'old_alias_name'    => [
                    'type'            => 'text',
                    "analyzer"        => "ik_max_word",
                    "search_analyzer" => "ik_smart"
                ],
                'old_addr'          => [
                    'type' => 'keyword'
                ],
                'status'            => [
                    'type' => 'integer'
                ]
            ]
        ];
    }
}
